// Package wechat 微信扩展包
package wechat

import (
	"bytes"
	m "ginmin/middleware"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"time"

	wxpay "github.com/mayoushang/wxpaygo"
)

type WechatPkg struct {
	AppId           string //公众号、小程序appid
	Secret          string //公众号、小程序密钥
	WxOpenAppid     string //开放平台appid
	WxOpenAppsecret string //开放平台密钥
	WxpayId         string //微信商户号
	WxpaySecret     string //微信支付密钥
	Types           string //类型，MP公众号 OPEN开放平台
	PayModel        string //支付类型 jsapi pc h5 app
}

var Wechat = map[string]string{
	"token":       "", //token
	"token_ctime": "", //过期时间
}

// Minlogin 微信小程序登录
func (pkg *WechatPkg) Minlogin(code string) string {
	appid := pkg.AppId
	secret := pkg.Secret
	url := "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code"
	resp, _ := http.Get(url)
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return ""
	}
	jsondata := string(body)
	openid := m.Json_decode_string(jsondata, "openid")
	if openid == "" {
		return ""
	}
	return openid
}

// Login 微信登录，返回url链接
func (pkg *WechatPkg) Login(redirect_uri string) string {
	redirect_uri = url.QueryEscape(redirect_uri)
	appid := pkg.AppId
	if pkg.PayModel == "pc" {
		appid = pkg.WxOpenAppid
		return "https://open.weixin.qq.com/connect/qrconnect?appid=" + appid + "&redirect_uri=" + redirect_uri + "&response_type=code&scope=snsapi_login&state=STATE#wechat_redirect"
	}
	return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=" + redirect_uri + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"
}

// Auth 微信返回code，获取用户信息
func (pkg *WechatPkg) Auth(code, redirect_uri string) map[string]string {
	appid := pkg.AppId
	secret := pkg.Secret
	if pkg.PayModel == "pc" {
		appid = pkg.WxOpenAppid
		secret = pkg.WxOpenAppsecret
	}

	urls := "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code"
	resp, _ := http.Get(urls)
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil
	}
	jsondata := string(body)
	token := m.Json_decode_string(jsondata, "access_token")
	openid := m.Json_decode_string(jsondata, "openid")
	if openid == "" {
		return nil
	}
	urls = "https://api.weixin.qq.com/sns/userinfo?access_token=" + token + "&openid=" + openid + "&lang=zh_CN"
	resp2, _ := http.Get(urls)
	defer resp2.Body.Close()
	body2, err2 := io.ReadAll(resp2.Body)
	if err2 != nil {
		return nil
	}
	jsondata = string(body2)
	jsonarray := m.Json_decode_array(jsondata)
	openid = m.Json_decode_string(jsondata, "openid")
	if openid == "" {
		return nil
	}
	userInfo := map[string]string{
		"openid":     jsonarray["openid"],
		"nickname":   jsonarray["nickname"],
		"sex":        jsonarray["sex"],
		"province":   jsonarray["province"],
		"city":       jsonarray["city"],
		"country":    jsonarray["country"],
		"headimgurl": jsonarray["headimgurl"],
		"unionid":    jsonarray["unionid"],
		"types":      pkg.Types,
	}
	return userInfo
}

// GoPay 发起支付
func (pkg *WechatPkg) GoPay(payInfo map[string]string) map[string]interface{} {
	appid := pkg.AppId
	paystr := make(map[string]interface{})
	notifyUrl := payInfo["notifyUrl"]
	price := m.ToInt(m.ToFloat64(payInfo["price"]) * 100)
	TradeType := wxpay.TradeTypeJSAPI
	if pkg.PayModel == "pc" {
		TradeType = wxpay.TradeTypeNative
	}
	if pkg.PayModel == "h5" {
		TradeType = wxpay.TradeTypeH5
	}
	if pkg.PayModel == "app" {
		appid = pkg.WxOpenAppid
		TradeType = wxpay.TradeTypeApp
	}
	cfg := &wxpay.Config{
		AppID:      appid,
		MchID:      pkg.WxpayId,
		AppSecret:  pkg.WxpaySecret,
		TradeType:  TradeType,
		NotifyURL:  notifyUrl,
		ServerAddr: "127.0.0.1",
	}
	if pkg.PayModel == "jsapi" {
		prepayID, _ := wxpay.JSAPITrade(cfg, payInfo["title"], payInfo["pay_id"], price, payInfo["mp_openid"], "114.114.114.114")
		paystr = map[string]interface{}{
			"appId":     appid,
			"timeStamp": wxpay.MakeUnixTime(),
			"nonceStr":  wxpay.MakeNonceStr(20),
			"package":   "prepay_id=" + prepayID,
			"signType":  "MD5",
			"paySign":   "",
		}
		paystr["paySign"] = wxpay.MakeSign(paystr, pkg.WxpaySecret)
	}
	if pkg.PayModel == "app" {
		prepayID, _ := wxpay.AppTrade(cfg, payInfo["title"], payInfo["pay_id"], price, "114.114.114.114")
		paystr = map[string]interface{}{
			"appId":     appid,
			"partnerid": pkg.WxpayId,
			"prepayid":  "" + prepayID,
			"package":   "Sign=WXPay",
			"noncestr":  wxpay.MakeNonceStr(20),
			"timestamp": wxpay.MakeUnixTime(),
			"sign":      "",
		}
		paystr["sign"] = wxpay.MakeSign(paystr, pkg.WxpaySecret)
	}
	if pkg.PayModel == "pc" {
		codeURL, _ := wxpay.NativeTrade(cfg, payInfo["title"], payInfo["pay_id"], price)
		paystr = map[string]interface{}{
			"codeURL": codeURL,
			"payApp":  "微信",
		}
	}
	if pkg.PayModel == "h5" {
		h5URL, _ := wxpay.H5Trade(cfg, payInfo["title"], payInfo["pay_id"], price, "114.114.114.114")
		paystr = map[string]interface{}{
			"h5URL": h5URL,
		}
	}
	return paystr
}

// Notify 支付结果
func (pkg *WechatPkg) Notify(req *http.Request) map[string]string {
	payInfo := make(map[string]string)
	cfg := &wxpay.Config{
		AppSecret: pkg.WxpaySecret,
	}
	res, err := wxpay.Notify(cfg, req)
	if err != nil {
		return nil
	}
	if res.ResultCode == "SUCCESS" {
		payInfo = map[string]string{
			"TotalFee":      strconv.Itoa(res.TotalFee),
			"TransactionID": res.TransactionID,
			"OutTradeNO":    res.OutTradeNO,
			"Attach":        res.Attach,
		}
		return payInfo
	}
	return nil
}

// Refund 退款
func (pkg *WechatPkg) Refund(money float64, total_fee float64, tradeNo string, out_refund_no string, desc string) bool {
	if money <= 0 {
		money = total_fee
	}
	cfg := &wxpay.Config{
		AppID:     pkg.AppId,
		MchID:     pkg.WxpayId,
		AppSecret: pkg.WxpaySecret,
	}
	cfg.LoadTLSConfig("cert/wxpay/apiclient_cert.pem", "cert/wxpay/apiclient_key.pem")

	_total_fee := m.ToInt(total_fee * 100)
	_money := m.ToInt(money * 100)
	res, err := wxpay.Refund(cfg, tradeNo, out_refund_no, _total_fee, _money, desc)
	if err != nil {
		return false
	}
	if res.ResultCode != "SUCCESS" {
		return false
	}
	return true
}

// 获取token
func (pkg *WechatPkg) getToken() string {
	nowtime := strconv.FormatInt(time.Now().Unix(), 10)
	token_time := m.ToInt(Wechat["token_ctime"]) + 6780
	if token_time < m.ToInt(nowtime) {
		urls := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + pkg.AppId + "&secret=" + pkg.Secret
		resp, _ := http.Get(urls)
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return ""
		}
		jsondata := string(body)
		access_token := m.Json_decode_string(jsondata, "access_token")
		Wechat["token"] = access_token
		Wechat["token_ctime"] = nowtime
	}
	return Wechat["token"]
}

// PostWx 模板消息
func (pkg *WechatPkg) PostWx(data map[string]interface{}) bool {
	urls := "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + pkg.getToken()
	jsonBytes := []byte(m.MapToJson(data))
	resp, err := http.Post(urls, "application/json;charset=UTF-8", bytes.NewReader(jsonBytes))
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return false
	}
	jsondata := string(body)
	errcode := m.Json_decode_int(jsondata, "errcode")
	if errcode != 0 {
		return false
	}
	return true
}
